/*
 * Copyright (C) 2014 luke.
 * 
 * This file is part of Silvie.
 * 
 * Silvie is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Silvie is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Silvie.  If not, see <http://www.gnu.org/licenses/>.
 */

package silvie.resources.chrmodel.chunks;

import java.util.ArrayList;
import java.util.List;

import javax.vecmath.Point3f;

import silvie.resources.chrmodel.structures.ShapePosition;
import utils.DataReader;

/**
 * AbstractChunk specialization for the "shape positions" chunk. This chunk
 * contains a parent-children structure which is used to position each shape in
 * the model. Prior to that, this chunk must first be mapped to the 0x7f06
 * chunk.
 * 
 * @author luke
 * @see Chunk0x7f06#mapPositions(Chunk0x7f05)
 */
public class Chunk0x7f05 extends AbstractChunk {
	private static final int POSITION_INFO_SIZE = 16;
	private static final int FIRST_POSITION_ID_OFFSET = 24;
	private static final int POSITIONS_COUNT_OFFSET = 4;
	private List<ShapePosition> positions;

	/**
	 * Chunk0x7f05 constructor.
	 * 
	 * @see AbstractChunk#AbstractChunk(int, byte[])
	 */
	public Chunk0x7f05(int id, byte[] data) {
		super(id, data);
		this.positions = new ArrayList<ShapePosition>();
	}

	@Override
	public int decode() {
		int positionsCount = DataReader.readInt(super.data, POSITIONS_COUNT_OFFSET);
		positionsCount--;
		for (int i = 0; i < positionsCount; i++) {
			int id = DataReader.readInt(super.data, FIRST_POSITION_ID_OFFSET + POSITION_INFO_SIZE * i);
			Point3f position = DataReader.readPoint3f(super.data, FIRST_POSITION_ID_OFFSET + DataReader.INT_SIZE + POSITION_INFO_SIZE * i);
			this.positions.add(new ShapePosition(id, position));
		}
		makePositionsAbsolute();
		return super.data.length;
	}

	/**
	 * Makes each position absolute, that is, independent from the parent
	 * position (this is to make {@link #getPositionAt(int)} faster).
	 */
	private void makePositionsAbsolute() {
		for (int i = 0; i < this.positions.size(); i++) {
			ShapePosition position = this.positions.get(i);
			int id = position.getId();
			if (id != 0) { // The 0th index has no parent
				if (id == i) { // The parent is the previous position
					position.offsetBy(this.positions.get(i - 1));
				} else if (id != -1) { // The parent is at [id - 1]
					position.offsetBy(this.positions.get(id - 1));
				}
			}
		}
	}

	/**
	 * Returns the position at index {@code i}. {@link #makePositionsAbsolute()}
	 * must be called first.
	 * 
	 * @param i the index of the position.
	 * @return the position at index {@code i}.
	 */
	public Point3f getPositionAt(int i) {
		if (this.positions.isEmpty()) {
			return new Point3f(0, 0, 0);
		}
		return this.positions.get(i).getPosition();
	}
}
